package com.mon4cc.simulation;

import com.mon4cc.simulation.dataranger.DataRanger;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @author Ping
 * @date 2022/12/19 12:09
 **/
public class SimulationConfig {
    public static String Art_Event_Name = "Null-Event" ;
    private SwappedEvents swappedEvents ;
    private Set<String> artificialEventInsertPoints ;
    private Set<String> removedEvents ;
    private Set<String> invalidatedDataAssignmentEvents ;
    private Map<String, DataRanger> dataRangerMap ;

    private long totalNumOfEvents ;
    private int numOfSwapped  ;
    private int numOfArtificialEvent ;
    private int numOfInvalidatedDataAssignment = 0 ;

    /**
     * Generates simulated data for written variables, based on {@code dataRangerMap}.
     * @param variables
     * @return
     */
    public Map<String, Object> simulatedDataFromDataObject(Set<String> variables) {
        Map<String, Object> simulatedData = new HashMap<>() ;
        variables.forEach(var -> {
            Object val = dataRangerMap.get(var).nextValue() ;
            Assert.notNull(val, "val should not null for data ranger, possibly since the data ranger was not config well.");
            simulatedData.put(var, val) ;
        }) ;
        return simulatedData ;
    }
    public String swappedTo(String from) {
        return this.swappedEvents.getAnother(from) ;
    }
    public boolean needToSwapped(String from) {
        if (ObjectUtils.isEmpty(this.swappedEvents)) {
            return false ;
        }
        return this.swappedEvents.involved(from) ;
    }


    public static class SimulationConfigBulder {
        private SimulationConfig config ;

        public SimulationConfigBulder() {
            this.config = new SimulationConfig() ;
        }
        public SimulationConfigBulder configSwappedEvents(SwappedEvents swappedEvents) {
            this.config.setSwappedEvents(swappedEvents);
            return this ;
        }

        public SimulationConfigBulder configSwappedEvents(String  firstEvent, String secondEvent) {
            this.config.setSwappedEvents(new SwappedEvents(firstEvent, secondEvent));
            return this ;
        }
        public SimulationConfigBulder configArtificialEventInsertPoints(String ... events) {
            this.config.setArtificalEventInsertPoints(new HashSet<>(Arrays.asList(events)));
            return this ;
        }
        public SimulationConfigBulder configRemovedEvents(String ... events) {
            this.config.setRemovedEvents(new HashSet<String>(Arrays.asList(events)));
            return this ;
        }
        public SimulationConfigBulder configInvalidatedDataAssignmentEvents(String ... events) {
            this.config.setInvalidatedDataAssignmentEvents(new HashSet<String>(Arrays.asList(events)));
            return this ;
        }
        public SimulationConfigBulder configTotalNumOfEvents(long length) {
            this.config.setTotalNumOfEvents(length);
            return this ;

        }
        public SimulationConfigBulder configDataRanger(Map<String, DataRanger> dataRangerMap) {
            this.config.setDataRangerMap(dataRangerMap);
            return this ;
        }
        public SimulationConfig build() {
            return this.config ;
        }

    }

    public Map<String, DataRanger> getDataRangerMap() {
        return dataRangerMap;
    }

    public void setDataRangerMap(Map<String, DataRanger> dataRangerMap) {
        this.dataRangerMap = dataRangerMap;
    }

    public SwappedEvents getSwappedEvents() {
        return swappedEvents;
    }

    public void setSwappedEvents(SwappedEvents swappedEvents) {
        this.swappedEvents = swappedEvents;
    }

    public boolean isNeedInsertArtificialEvent(String nodeName) {
        if (ObjectUtils.isEmpty(this.artificialEventInsertPoints)) {
            return false ;
        }
        return this.artificialEventInsertPoints.contains(nodeName);
    }

    public void setRemovedEvents(Set<String> removedEvents) {
        this.removedEvents = removedEvents;
    }

    public void setInvalidatedDataAssignmentEvents(Set<String> invalidatedDataAssignmentEvents) {
        this.invalidatedDataAssignmentEvents = invalidatedDataAssignmentEvents;
    }
    public boolean containedInInvalidatedDataAssignementEvents(String instanceName) {
        if (ObjectUtils.isEmpty(this.invalidatedDataAssignmentEvents)) {
            return false ;
        }
        return this.invalidatedDataAssignmentEvents.contains(instanceName) ;
    }

    public long getTotalNumOfEvents() {
        return totalNumOfEvents;
    }

    public void setTotalNumOfEvents(long totalNumOfEvents) {
        this.totalNumOfEvents = totalNumOfEvents;
    }

    public int getNumOfSwapped() {
        return numOfSwapped;
    }

    public void setNumOfSwapped(int numOfSwapped) {
        this.numOfSwapped = numOfSwapped;
    }

    public int getNumOfArtificialEvent() {
        return numOfArtificialEvent;
    }

    public void setNumOfArtificialEvent(int numOfArtificialEvent) {
        this.numOfArtificialEvent = numOfArtificialEvent;
    }

    public int getNumOfInvalidatedDataAssignment() {
        return numOfInvalidatedDataAssignment;
    }

    public void setNumOfInvalidatedDataAssignment(int numOfInvalidatedDataAssignment) {
        this.numOfInvalidatedDataAssignment = numOfInvalidatedDataAssignment;
    }

    public Set<String> getArtificalEventInsertPoints() {
        return artificialEventInsertPoints;
    }

    public void setArtificalEventInsertPoints(Set<String> artificalEventInsertPoints) {
        this.artificialEventInsertPoints = artificalEventInsertPoints;
    }

    static class SwappedEvents {
        String one;
        String another ;

        public SwappedEvents(String one, String another) {
            this.one = one ;
            this.another = another ;
        }
        public boolean involved(String name) {
            return this.one.equals(name) || this.another.equals(name) ;
        }

        public String getAnother(String eventName) {
            if (! this.involved(eventName)) {
                return eventName ;
            }
            if (this.one.equals(eventName)) {
                return this.another ;
            } else {
                return this.one ;
            }
        }


    }




}
